package starfield.modpack.web;

import starfield.modpack.web.SyncingException;
import starfield.modpack.web.SyncingTask;
import starfield.modpack.web.WebModpack;
import starfield.modpack.Modpack;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.logging.Level;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;

public class FTPSyncingTask extends SyncingTask {
	private boolean ssl;
	private boolean isImplicit;

	public FTPSyncingTask(WebModpack webModpack, boolean silent, boolean ssl, boolean isImplicit) {
		super(webModpack, silent);
		this.ssl = ssl;
		this.isImplicit = isImplicit;
	}

	@Override
	protected Void doInBackground() throws IOException, SyncingException, InterruptedException {
		FTPClient ftp;

		if (ssl) {
			ftp = new FTPSClient(isImplicit);
			
			if (isImplicit) {
				publish("Created FTPS client.", Level.INFO);
			} else {
				publish("Created FTPES client.", Level.INFO);
			}
		} else {
			ftp = new FTPClient();
			
			publish("Created FTP client.", Level.INFO);
		}
		
		publish("Connecting to '" + modpack.getHostname() + ":" + modpack.getPort() + "'...", Level.INFO);
		
		ftp.connect(modpack.getHostname(), modpack.getPort());

		publish("Connection successful.", Level.FINE);
		
		if (modpack.requiresLogin()) {
			publish("Logging in as '" + modpack.getUsername() + "'...", Level.INFO);
			
			if (ftp.login(modpack.getUsername(), modpack.getPassword())) {
				publish("Login successful.", Level.FINE);
			} else {
				ftp.logout();
				ftp.disconnect();
				throw new SyncingException("Failed FTP login.");
			}
		} else {
			publish("Attempting anonymous login...", Level.INFO);
		}

		int reply = ftp.getReplyCode();

		if (!FTPReply.isPositiveCompletion(reply)) {
			ftp.disconnect();
			throw new SyncingException("FTP error: " + ftp.getReplyStrings()[0]);
		} else if (!modpack.requiresLogin) {
			publish("Anonymous login successful.", Level.FINE);
		}

		publish("Listing files...", Level.INFO);
		
		FTPFile[] ftpFiles = ftp.listFiles();  

		reply = ftp.getReplyCode();

		if (!FTPReply.isPositiveCompletion(reply)) {
			ftp.disconnect();
			throw new SyncingException("FTP error: " + ftp.getReplyStrings()[0]);
		} else {
			publish("File listing successful.", Level.FINE);
		}
		
		publish("Looking for files to sync...", Level.INFO);
		
		for (FTPFile remoteFile : ftpFiles) {
			if (remoteFile == null || !remoteFile.isFile()) {
				continue;
			}

			if (Modpack.isModpackFilename(remoteFile.getName())) {
				File localFile = new File(modpack.getDirectoryPath() + File.separator + remoteFile.getName());

				Date remoteDate = new Date(remoteFile.getTimestamp().getTimeInMillis());
				Date localDate = new Date(localFile.lastModified());

				boolean localFileExists = localFile.exists() && localFile.isFile();

				if (!localFileExists) {
					publish("Downloading file '" + remoteFile.getName() + "'...", Level.INFO);
				} else if (remoteDate.after(localDate)) {
					publish("Updating file '" + remoteFile.getName() + "'...", Level.INFO);
				} else {
					publish("Local file '" + remoteFile.getName() + "' is up to date.", Level.INFO);
				}

				if (!localFileExists || remoteDate.after(localDate))
				{
					try (FileOutputStream output = new FileOutputStream(localFile)) {
						if (ftp.retrieveFile(remoteFile.getName(), output)) {
							publish("File transfer successful.", Level.FINE);
						} else {
							publish("File transfer failed:" + System.lineSeparator() + ftp.getReplyStrings()[0], Level.WARNING);
						}
					}
				}
			}
		}
		
		publish("Looking for obsolete files to delete...", Level.INFO);
		
		for (File localFile : modpack.getDirectory().listFiles()) {
			boolean remoteFileExists = false;

			for (FTPFile remoteFile : ftpFiles) {
				if (remoteFile != null && remoteFile.isFile() && remoteFile.getName().equals(localFile.getName())) {
					remoteFileExists = true;
					break;
				}
			}

			if (!remoteFileExists) {
				localFile.delete();
				publish("Deleted obsolete file '" + localFile.getName() + "'.", Level.INFO);
			}
		}
		
		publish("Logging out...", Level.INFO);

		ftp.logout();
		
		publish("Disconnecting...", Level.INFO);
		
		ftp.disconnect();
		
		publish("Sync successful.", Level.FINE);

		return null;
	}
}
